package org.lex.reflect.browsing.view;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import javax.swing.Icon;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTree;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;


import org.lex.reflect.browsing.model.ClassPathTreeFactory;
import org.lex.reflect.browsing.model.Node;
import org.lex.reflect.browsing.util.ClassHelper;
import org.lex.reflect.browsing.util.IconCache;
import org.lex.swing.ContainerFactory;
import org.lex.swing.Swings;
import org.lex.utils.ToString;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * A simple class browser.
 */
public class ClassBrowserPane extends JPanel// implements ClassPathListener
{
	private static Logger log = LoggerFactory.getLogger(TreeHelper.class);

	private JSplitPane mainSplitPane = null;
	private ClassMembersPane membersPane = null;
	private JTree classTree = null;

	public static void main(String[] args) throws Exception {
		Swings.setUseBoldMetalFont(false);
		Swings.show(new ClassBrowserPane());
	}

	public ClassBrowserPane() {
		super(new BorderLayout());
		this.initialize();
	}

	public void setSelectedClass(String className) {
		Class<?> clazz = null;
		try {
			clazz = ClassHelper.getClass(className);
		} catch (Exception e) {
			e.printStackTrace();
			return;
		}
		if (clazz == null) {
			// not found?
			System.err.println("class not found: " + className);
			return;
		}
		this.getMembersPane().setClass(clazz);
	}

	private void initialize() {
		this.add(this.getMainSplitPane(), BorderLayout.CENTER);
	}

	private JSplitPane getMainSplitPane() {
		if (null == this.mainSplitPane) {
			mainSplitPane = ContainerFactory.createBorderlessSplitPane(5);
			mainSplitPane.setName(this.getClass().getName() + ".mainSplitPane");
			mainSplitPane.setResizeWeight(0.2D);
			mainSplitPane.setDividerLocation(160);
			mainSplitPane.setOrientation(JSplitPane.HORIZONTAL_SPLIT);
			mainSplitPane.setLeftComponent(new JScrollPane(this.getClassTree()));
			mainSplitPane.setRightComponent(this.getMembersPane());
		}
		return this.mainSplitPane;
	}

	private ClassMembersPane getMembersPane() {
		if (null == membersPane)
			membersPane = new ClassMembersPane();
		return membersPane;
	}

	private JTree getClassTree() {
		if (null == classTree) {
			TreeNode root = new ClassPathTreeFactory().buildSystemAndUserClassPaths();
			classTree = new JTree(new DefaultTreeModel(root, false));
			classTree.setRootVisible(false);
			classTree.setCellRenderer(new DefaultTreeCellRenderer() {
				private Icon classIcon = IconCache.getIcon(IconCache.CLASS);
				private Icon packageIcon = IconCache.getIcon(IconCache.PACKAGE);

				public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row,
						boolean hasFocus) {
					super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
					if (leaf)
						this.setIcon(classIcon);
					else
						this.setIcon(packageIcon);
					return this;
				}
			});
			classTree.addMouseListener(new MouseAdapter() {
				public void mouseClicked(MouseEvent e) {
					TreePath tp = classTree.getPathForLocation(e.getX(), e.getY());
					String className = new TreeHelper().getFullClassNameByTreePath(tp);
					log.info("selected class name=" + className);
					if (null != className) {
						setSelectedClass(className);
					}
				}
			});
		}
		return classTree;
	}

	private static class TreeHelper {

		private String getFullClassNameByTreePath(TreePath tp) {
			if (null == tp) {
				log.debug("Nothing to do: tree selection is NULL");
				return null;
			}
			Node last = (Node) tp.getLastPathComponent();
			if (null == last) {
				log.debug("Nothing to do: last path node is NULL");
				return null;
			}
			if (0 != last.getChildCount()) {
				log.debug("Nothing to do: last path node is NOT LEAF");
				return null;
			}

			Object[] path = tp.getPath();
			List<String> pkgs = new ArrayList<String>(path.length);
			for (int i = path.length - 1; i >= 0; i--) {
				Node node = (Node) path[i];
				if (node.getType() == Node.Type.PACKAGE || node.getType() == Node.Type.CLASS) {
					pkgs.add((String) node.getUserObject());
				}
			}
			Collections.reverse(pkgs);
			return ToString.toString(pkgs, "", "", ".");
		}
	}
}
